package com.kingmed.kmss.common.service.obs.impl;

import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.kingmed.kmss.common.constant.common.OperatorConstants;
import com.kingmed.kmss.common.exception.BusinessException;
import com.kingmed.kmss.common.service.obs.HuaWeiOBSConfig;
import com.kingmed.kmss.common.service.obs.HuaWeiYunOBSService;
import com.kingmed.kmss.common.utils.uuid.UUID;
import com.obs.services.ObsClient;
import com.obs.services.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Description: 华为云OBS服务业务层
 */
@Slf4j
@Service
public class HuaWeiYunOBSServiceImpl implements HuaWeiYunOBSService {

    @Autowired
    private HuaWeiOBSConfig huaWeiOBSConfig;

    /**
     * 删除
     */
    @Override
    public boolean delete(String filePathAndName) {
        ObsClient obsClient = null;
        try {
            // 创建ObsClient实例
            obsClient = huaWeiOBSConfig.getInstance();
            // obs删除
            obsClient.deleteObject(huaWeiOBSConfig.getBucketName(), filePathAndName);
        } catch (Exception e) {
            log.error("obs删除保存失败", e);
            throw new BusinessException("obs删除保存失败：" + e.getMessage());
        } finally {
            huaWeiOBSConfig.destroy(obsClient);
        }
        return true;
    }

    /**
     * 批量删除
     */
    @Override
    public boolean delete(List<String> filePathAndNames) {
        ObsClient obsClient = null;
        try {
            obsClient = huaWeiOBSConfig.getInstance();
            DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(huaWeiOBSConfig.getBucketName());
            filePathAndNames.forEach(deleteObjectsRequest::addKeyAndVersion);
            // 批量删除请求
            obsClient.deleteObjects(deleteObjectsRequest);
            return true;
        } catch (Exception e) {
            log.error("obs删除保存失败", e);
            throw new BusinessException("obs删除保存失败：" + e.getMessage());
        } finally {
            huaWeiOBSConfig.destroy(obsClient);
        }
    }

    /**
     * 上传
     */
    @Override
    public Map<String, String> fileUpload(MultipartFile uploadFile, String filePath) {
        Map<String, String> resultMap = new HashMap<>();

        // 文件存放路径 + 毫秒时间戳 + 文件名称
        String fileName = uploadFile.getOriginalFilename();
        String obsFilePathAndName = filePath + System.currentTimeMillis() + "_" + fileName;

        ObsClient obsClient = null;
        try {
            String bucketName = huaWeiOBSConfig.getBucketName();
            obsClient = huaWeiOBSConfig.getInstance();
            // 判断桶是否存在
            boolean exists = obsClient.headBucket(bucketName);
            if (!exists) {
                // 若不存在，则创建桶
                HeaderResponse response = obsClient.createBucket(bucketName);
                log.info("创建桶成功" + response.getRequestId());
            }
            InputStream inputStream = uploadFile.getInputStream();
            long available = inputStream.available();
            PutObjectRequest request = new PutObjectRequest(bucketName, obsFilePathAndName, inputStream);

            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength(available);
            request.setMetadata(objectMetadata);
            // 设置对象访问权限为公共读
            request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
            PutObjectResult result = obsClient.putObject(request);

            // 读取该已上传对象的URL
            log.info("已上传对象的URL" + result.getObjectUrl());
            resultMap.put("obsUrl", result.getObjectUrl());
            resultMap.put("fileName", fileName);
            resultMap.put("attachTypeValue", fileName != null ? fileName.substring(fileName.lastIndexOf(".") + 1) : "");
            resultMap.put("obsFileName", obsFilePathAndName);
            return resultMap;
        } catch (Exception e) {
            log.error("上传失败", e);
            throw new BusinessException("上传失败：" + e.getMessage());
        } finally {
            huaWeiOBSConfig.destroy(obsClient);
        }
    }

    /**
     * 上传
     *
     * @param base64Img
     * @param filePath
     * @return
     */
    @Override
    public Map<String, String> fileUpload(String base64Img, String filePath) {
        Map<String, String> resultMap = new HashMap<>();

        // 文件存放路径 + 毫秒时间戳 + 文件名称
        String fileName = UUID.fastUUID().toString().substring(0, 15).concat(".png");
        String obsFilePathAndName = filePath + System.currentTimeMillis() + "_" + fileName;

        ObsClient obsClient = null;
        try {
            String bucketName = huaWeiOBSConfig.getBucketName();
            obsClient = huaWeiOBSConfig.getInstance();
            // 判断桶是否存在
            boolean exists = obsClient.headBucket(bucketName);
            if (!exists) {
                // 若不存在，则创建桶
                HeaderResponse response = obsClient.createBucket(bucketName);
                log.info("创建桶成功" + response.getRequestId());
            }

            InputStream inputStream = base64ToInputStream(base64Img);
            long available = inputStream.available();
            PutObjectRequest request = new PutObjectRequest(bucketName, obsFilePathAndName, inputStream);

            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength(available);
            request.setMetadata(objectMetadata);
            // 设置对象访问权限为公共读
            request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
            PutObjectResult result = obsClient.putObject(request);

            // 读取该已上传对象的URL
            log.info("已上传对象的URL" + result.getObjectUrl());
            resultMap.put("obsUrl", result.getObjectUrl());
            resultMap.put("fileName", fileName);
            resultMap.put("attachTypeValue", fileName != null ? fileName.substring(fileName.lastIndexOf(".") + 1) : "");
            resultMap.put("obsFileName", obsFilePathAndName);
            resultMap.put("fileSize", available + "");
            resultMap.put("contentType", MediaType.IMAGE_PNG.toString());
            return resultMap;
        } catch (Exception e) {
            log.error("上传失败", e);
            throw new BusinessException("上传失败：" + e.getMessage());
        } finally {
            huaWeiOBSConfig.destroy(obsClient);
        }
    }

    @Override
    public Map<String, String> fileUpload(String base64Img, String filePath, String fileType) {
        Map<String, String> resultMap = new HashMap<>();

        // 文件存放路径 + 毫秒时间戳 + 文件名称
        String fileName = UUID.fastUUID().toString().substring(0, 15).concat(fileType);
        String obsFilePathAndName = filePath + System.currentTimeMillis() + "_" + fileName;

        ObsClient obsClient = null;
        try {
            log.info("配置信息：accessKey:{},securityKey:{},endPoint:{},bucketName:{}",
                    huaWeiOBSConfig.getAccessKey(), huaWeiOBSConfig.getSecurityKey(), huaWeiOBSConfig.getEndPoint(), huaWeiOBSConfig.getBucketName());
            String bucketName = huaWeiOBSConfig.getBucketName();
            obsClient = huaWeiOBSConfig.getInstance();
            // 判断桶是否存在
            boolean exists = obsClient.headBucket(bucketName);
            if (!exists) {
                // 若不存在，则创建桶
                HeaderResponse response = obsClient.createBucket(bucketName);
                log.info("创建桶成功" + response.getRequestId());
            }

            InputStream inputStream = base64ToInputStream(base64Img);
            long available = inputStream.available();
            PutObjectRequest request = new PutObjectRequest(bucketName, obsFilePathAndName, inputStream);

            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength(available);
            request.setMetadata(objectMetadata);
            // 设置对象访问权限为公共读
            request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
            PutObjectResult result = obsClient.putObject(request);

            // 读取该已上传对象的URL
            log.info("已上传对象的URL" + result.getObjectUrl());
            resultMap.put("obsUrl", result.getObjectUrl());
            resultMap.put("fileName", fileName);
            resultMap.put("attachTypeValue", fileName.substring(fileName.lastIndexOf(".") + 1));
            resultMap.put("obsFileName", obsFilePathAndName);
            resultMap.put("fileSize", available + "");
            return resultMap;
        } catch (Exception e) {
            log.error("上传失败", e);
            throw new BusinessException("上传失败：" + e.getMessage());
        } finally {
            huaWeiOBSConfig.destroy(obsClient);
        }
    }

    /**
     * base64转流
     *
     * @param base64Img
     * @return
     */
    private InputStream base64ToInputStream(String base64Img) {
        InputStream is = null;
        byte[] b = null;
        try {
            b = Base64Decoder.decode(base64Img.substring(base64Img.indexOf(OperatorConstants.DH_KEY) + 1));
            is = new ByteArrayInputStream(b);
            return is;
        } catch (Exception e) {
            throw new BusinessException("base64转流失败：" + e.getMessage());
        }
    }

    /**
     * 文件下载并反馈给前端
     *
     * @param filePathAndName 文件保存路径+文件名称
     * @param type            type=0下载后浏览器直接打开,type=1下载
     */
    @Override
    public void fileDownload(HttpServletRequest request, HttpServletResponse response, String filePathAndName, Integer type) throws IOException {
        ObsClient obsClient = null;
        try {
            String bucketName = huaWeiOBSConfig.getBucketName();
            obsClient = huaWeiOBSConfig.getInstance();
            ObsObject obsObject = obsClient.getObject(bucketName, filePathAndName);
            InputStream inputStream = obsObject.getObjectContent();

            // 反馈给前端
            BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            if (inputStream == null) {
                return;
            }
            // 为防止 文件名出现乱码
            final String userAgent = request.getHeader("USER-AGENT");
            // IE浏览器
            if (StrUtil.contains(userAgent, "MSIE")) {
                filePathAndName = URLEncoder.encode(filePathAndName, "UTF-8");
            } else {
                // google、火狐浏览器
                if (StrUtil.contains(userAgent, "Mozilla")) {
                    filePathAndName = new String(filePathAndName.getBytes(), "ISO8859-1");
                } else {
                    // 其他浏览器
                    filePathAndName = URLEncoder.encode(filePathAndName, "UTF-8");
                }
            }
            // 设置让浏览器弹出下载提示框，而不是直接在浏览器中打开, type==0下载后浏览器直接查看
            response.setContentType("application/" + (type == 0 ? "octet-stream" : "x-download"));
            // 文件obs存储名称 毫秒时间戳 + "_" + 文件名称
            String fileName = filePathAndName.substring(filePathAndName.indexOf("_") + 1);
            response.addHeader("Content-Disposition", (type == 0 ? "inline" : "attachment") + ";filename=" + fileName);

            IoUtil.copy(inputStream, outputStream);
        } catch (Exception e) {
            log.error("obs文件下载失败", e);
            throw new BusinessException("obs文件下载失败：" + e.getMessage());
        } finally {
            huaWeiOBSConfig.destroy(obsClient);
        }
    }

    /**
     * 文件下载获取文件流
     *
     * @param filePathAndName 文件保存路径+文件名称
     */
    @Override
    public InputStream fileDownload(String filePathAndName) {
        ObsClient obsClient = null;
        try {
            String bucketName = huaWeiOBSConfig.getBucketName();
            obsClient = huaWeiOBSConfig.getInstance();
            ObsObject obsObject = obsClient.getObject(bucketName, filePathAndName);

            return obsObject.getObjectContent();
        } catch (Exception e) {
            log.error("obs文件下载失败", e);
            throw new BusinessException("obs文件下载失败：" + e.getMessage());
        } finally {
            huaWeiOBSConfig.destroy(obsClient);
        }
    }

    /**
     * 批量下载文件
     *
     * @param filePathAndNames
     * @param response
     * @throws Exception
     */
    @Override
    public void downLoadBatchToZip(String filePathAndNames, HttpServletResponse response) throws Exception {
        response.reset();
        response.setHeader("Content-Disposition", "attachment;filename="
                + URLEncoder.encode("multiple-file.zip", "UTF-8"));
        response.setHeader("Content-Type", "application/octet-stream");
        ObsClient obsClient = null;
        try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
            String[] files = filePathAndNames.indexOf(",") != -1 ? filePathAndNames.split(",") : new String[]{filePathAndNames};
            String bucketName = huaWeiOBSConfig.getBucketName();
            obsClient = huaWeiOBSConfig.getInstance();
            //todo filePathAndName 包含了path和name？ 多文件传参逗号分割？
            for (String fileName : files) {
                ObsObject obsObject = obsClient.getObject(bucketName, fileName);
                InputStream inputStream = obsObject.getObjectContent();
                zos.putNextEntry(new ZipEntry(fileName));
                int len;
                // 读入需要下载的文件的内容，打包到zip文件
                byte[] buffer = new byte[1024];
                while ((len = inputStream.read(buffer)) > 0) {
                    zos.write(buffer, 0, len);
                }
                zos.closeEntry();
                inputStream.close();
            }

        } catch (Exception e) {
            log.error("obs文件下载失败", e);
            throw new BusinessException("obs文件下载失败：" + e.getMessage());
        } finally {
            huaWeiOBSConfig.destroy(obsClient);
        }

    }

    /**
     * 文件批量下载并压缩成zip
     *
     * @param filePathAndNames
     * @param response
     * @throws Exception
     */
    @Override
    public void downLoadBatchToZip(List<String> filePathAndNames, HttpServletResponse response) throws Exception {
        response.reset();
        response.setHeader("Content-Disposition", "attachment;filename="
                + URLEncoder.encode("multiple-file.zip", "UTF-8"));
        response.setHeader("Content-Type", "application/octet-stream");
        ObsClient obsClient = null;
        try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
            String bucketName = huaWeiOBSConfig.getBucketName();
            obsClient = huaWeiOBSConfig.getInstance();
            for (String fileName : filePathAndNames) {
                ObsObject obsObject = obsClient.getObject(bucketName, fileName);
                InputStream inputStream = obsObject.getObjectContent();
                zos.putNextEntry(new ZipEntry(fileName.substring(fileName.lastIndexOf("/") + 1)));
                int len;
                // 读入需要下载的文件的内容，打包到zip文件
                byte[] buffer = new byte[1024];
                while ((len = inputStream.read(buffer)) > 0) {
                    zos.write(buffer, 0, len);
                }
                zos.closeEntry();
                inputStream.close();
            }
        } catch (Exception e) {
            log.error("obs文件下载失败", e);
            throw new BusinessException("obs文件下载失败：" + e.getMessage());
        } finally {
            huaWeiOBSConfig.destroy(obsClient);
        }
    }

}
